import logging, time, thread
from repoze.bfg.request import Request
from sqlalchemy.orm import scoped_session
from hurryoff.db.LogRecord import LogRecord
from sqlalchemy.engine import create_engine
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm.session import sessionmaker

dbConnectionString = None
dbLogHandler = None
class DBLogHandler(logging.Handler):

    def __init__(self, dbConnectionString):
        logging.Handler.__init__(self)
        self.engine = create_engine(dbConnectionString)
        self.connection = None
        self.session = None
        self.retryTime = None
        self.retryStart = 1.0
        self.retryMax = 30.0
        self.retryFactor = 2.0

    def createConnection(self):
        now = time.time()
        if self.retryTime is None:
            attempt = 1
        else:
            attempt = (now >= self.retryTime)
        if attempt:
            try:
                self.connection = self.engine.connect()
                self.session = scoped_session(sessionmaker(bind=self.engine, autoflush=True))
                self.retryTime = None
            except:
                if self.retryTime is None:
                    self.retryPeriod = self.retryStart
                else:
                    self.retryPeriod = self.retryPeriod * self.retryFactor
                    if self.retryPeriod > self.retryMax:
                        self.retryPeriod = self.retryMax
                self.retryTime = now + self.retryPeriod

    def write(self, record):
        if self.connection is None:
            self.createConnection()
        if self.connection and self.session:
            try:
                ip = record.args['remote_addr'] if isinstance(record.args, dict) else None
                self.session.add(LogRecord(record.levelname, record.name, record.msg, ip))
                self.session.commit()
            except SQLAlchemyError:
                self.session.close()
                self.session = None
                self.connection = None

    def handleError(self, record):
        if self.session and self.connection:
            self.session.close()
            self.session = None
            self.connection = None
        else:
            logging.Handler.handleError(self, record)

    def emit(self, record):
        try:
            thread.start_new_thread(self.write, (record,))
        except:
            self.handleError(record)

    def close(self):
        logging.Handler.close(self)

_log = {}
def functionTracer(function):
    moduleName = function.__module__
    if not _log.has_key(moduleName):
        _log[moduleName] = module_logger(moduleName)
    def wrapped(*args, **kwargs):
        request = ([arg for arg in args if isinstance(arg, Request)][:1] or [None])[0]
        _log[moduleName].info("start -- %s" % function.func_name, {'remote_addr': request.remote_addr if request else None})
        result = function(*args, **kwargs)
        _log[moduleName].info("end -- %s" % function.func_name, {'remote_addr':request.remote_addr if request else None})
        return result
    wrapped.__doc__ = function.__doc__
    return wrapped

rootlogger = logging.getLogger('hurryoff')
if rootlogger.level == logging.NOTSET:
    rootlogger.setLevel(logging.WARN)

default_enabled = False
def default_logging(name):
    global default_enabled
    if logging.getLogger(name).getEffectiveLevel() < logging.WARN:
        default_enabled = True
    if not default_enabled:
        default_enabled = True
        handler = DBLogHandler(dbConnectionString)
        rootlogger.addHandler(handler)

def module_logger(moduleName):
    global dbLogHandler
    global dbConnectionString
    log = logging.getLogger(moduleName)
    if dbLogHandler is None:
        dbLogHandler = DBLogHandler(dbConnectionString)
    log.addHandler(dbLogHandler)
    log.setLevel(logging.INFO)
    return log

def instance_logger(instance, echoflag=None):
    name = "%s.%s.0x...%s" % (instance.__class__.__module__,
                             instance.__class__.__name__,
                             hex(id(instance))[-4:])

    if echoflag is not None:
        l = logging.getLogger(name)
        if echoflag == 'debug':
            default_logging(name)
            l.setLevel(logging.DEBUG)
        elif echoflag:
            default_logging(name)
            l.setLevel(logging.INFO)
        elif not echoflag:
            l.setLevel(logging.NOTSET)
    else:
        l = logging.getLogger(name)
    instance._should_log_debug = l.isEnabledFor(logging.DEBUG)
    instance._should_log_info = l.isEnabledFor(logging.INFO)
    return l
